import { defined } from '../../../Source/Cesium.js';
import { TimeInterval } from '../../../Source/Cesium.js';
import createViewer from '../../createViewer.js';
import DomEventSimulator from '../../DomEventSimulator.js';
import pollToPromise from '../../pollToPromise.js';
import { viewerDragDropMixin } from '../../../Source/Cesium.js';

describe('Widgets/Viewer/viewerDragDropMixin', function() {

    var container;
    var viewer;
    beforeEach(function() {
        container = document.createElement('div');
        container.id = 'container';
        container.style.display = 'none';
        document.body.appendChild(container);

        //Impersonate FileReader for drag and drop tests
        var fakeFileReader = jasmine.createSpyObj('FileReader', ['readAsText']);
        fakeFileReader.readAsText = function(file) {
            if (defined(file.czmlString)) {
                this.onload({
                    target : {
                        result : file.czmlString
                    }
                });
            } else {
                this.onerror({
                    target : {
                        error : file.errorMessage
                    }
                });
            }
        };
        spyOn(window, 'FileReader').and.returnValue(fakeFileReader);
    });

    afterEach(function() {
        if (viewer && !viewer.isDestroyed()) {
            viewer = viewer.destroy();
        }

        document.body.removeChild(container);
    });

    it('mixin sets default values', function() {
        viewer = createViewer(container);
        viewer.extend(viewerDragDropMixin);
        expect(viewer.dropTarget).toBe(viewer.container);
        expect(viewer.dropEnabled).toEqual(true);
        expect(viewer.clearOnDrop).toEqual(true);
        expect(viewer.clampToGround).toEqual(true);
        expect(viewer.flyToOnDrop).toEqual(true);
    });

    it('clearOnDrop defaults to true when dataSourceBrowser is not used', function() {
        viewer = createViewer(container, {
            dataSourceBrowser : false
        });
        viewer.extend(viewerDragDropMixin);
        expect(viewer.clearOnDrop).toEqual(true);
    });

    it('mixin sets option values', function() {
        viewer = createViewer(container);
        viewer.extend(viewerDragDropMixin, {
            dropTarget : document.body,
            clearOnDrop : false,
            clampToGround : false,
            flyToOnDrop: false
        });
        expect(viewer.dropTarget).toBe(document.body);
        expect(viewer.dropEnabled).toEqual(true);
        expect(viewer.clearOnDrop).toEqual(false);
        expect(viewer.clampToGround).toEqual(false);
        expect(viewer.flyToOnDrop).toEqual(false);
    });

    it('mixin works with dropTarget id string', function() {
        viewer = createViewer(container);
        viewer.extend(viewerDragDropMixin, {
            dropTarget : 'container'
        });
        expect(viewer.dropTarget).toBe(container);
    });

    var czml1 = [{
        id : 'document',
        version : '1.0',
        clock : {
            interval : '2000-01-01/2001-01-01'
        }
    }, {
        id : 'test'
    }];

    var czml2 = [{
        id : 'document',
        version : '1.0',
        clock : {
            interval : '2000-01-02/2001-01-02'
        }
    }, {
        id : 'test2'
    }];

    it('handleDrop processes drop event', function() {
        var mockEvent = {
            dataTransfer : {
                files : [{
                    name : 'czml1.czml',
                    czmlString : JSON.stringify(czml1)
                }]
            },
            stopPropagation : function() {
            },
            preventDefault : function() {
            }
        };

        viewer = createViewer(container);
        viewer.extend(viewerDragDropMixin);

        DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

        return pollToPromise(function() {
            return viewer.dataSources.length === 1;
        }).then(function() {
            var dataSource = viewer.dataSources.get(0);
            var interval = TimeInterval.fromIso8601({
                iso8601 : czml1[0].clock.interval
            });
            expect(dataSource.entities.getById('test')).toBeDefined();
            expect(dataSource.clock.startTime).toEqual(interval.start);
            expect(dataSource.clock.stopTime).toEqual(interval.stop);
        });
    });

    it('handleDrop processes drop event with multiple files', function() {
        var mockEvent = {
            dataTransfer : {
                files : [{
                    name : 'czml1.czml',
                    czmlString : JSON.stringify(czml1)
                }, {
                    name : 'czml2.czml',
                    czmlString : JSON.stringify(czml2)
                }]
            },
            stopPropagation : function() {
            },
            preventDefault : function() {
            }
        };

        viewer = createViewer(container);
        viewer.extend(viewerDragDropMixin);

        DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

        return pollToPromise(function() {
            return viewer.dataSources.length === 2;
        }).then(function() {
            var source1 = viewer.dataSources.get(0);
            var source2 = viewer.dataSources.get(1);
            expect(source1.entities.getById('test')).toBeDefined();
            expect(source2.entities.getById('test2')).toBeDefined();
            //Interval of first file should be used.
            var interval = TimeInterval.fromIso8601({
                iso8601 : czml1[0].clock.interval
            });
            expect(source1.clock.startTime).toEqual(interval.start);
            expect(source1.clock.stopTime).toEqual(interval.stop);
        });
    });

    it('handleDrop obeys clearOnDrop', function() {
        var mockEvent = {
            dataTransfer : {
                files : [{
                    name : 'czml1.czml',
                    czmlString : JSON.stringify(czml1)
                }, {
                    name : 'czml2.czml',
                    czmlString : JSON.stringify(czml2)
                }]
            },
            stopPropagation : function() {
            },
            preventDefault : function() {
            }
        };

        viewer = createViewer(container);
        viewer.extend(viewerDragDropMixin);

        DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

        return pollToPromise(function() {
            return viewer.dataSources.length === 2;
        }).then(function() {
            var source1 = viewer.dataSources.get(0);
            var source2 = viewer.dataSources.get(1);
            expect(source1.entities.getById('test')).toBeDefined();
            expect(source2.entities.getById('test2')).toBeDefined();
            //Interval of first file should be used.
            var interval = TimeInterval.fromIso8601({
                iso8601 : czml1[0].clock.interval
            });
            expect(source1.clock.startTime).toEqual(interval.start);
            expect(source1.clock.stopTime).toEqual(interval.stop);

            viewer.clearOnDrop = false;
            DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

            return pollToPromise(function() {
                return viewer.dataSources.length === 4;
            }).then(function() {
                var source1 = viewer.dataSources.get(0);
                var source2 = viewer.dataSources.get(1);
                var source3 = viewer.dataSources.get(2);
                var source4 = viewer.dataSources.get(3);

                expect(source1.entities.getById('test')).toBeDefined();
                expect(source2.entities.getById('test2')).toBeDefined();
                expect(source3.entities.getById('test')).toBeDefined();
                expect(source4.entities.getById('test2')).toBeDefined();

                viewer.clearOnDrop = true;
                DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

                return pollToPromise(function() {
                    return viewer.dataSources.length === 2;
                }).then(function() {
                    var source1 = viewer.dataSources.get(0);
                    var source2 = viewer.dataSources.get(1);
                    expect(source1.entities.getById('test')).toBeDefined();
                    expect(source2.entities.getById('test2')).toBeDefined();
                    //Interval of first file should be used.
                    var interval = TimeInterval.fromIso8601({
                        iso8601 : czml1[0].clock.interval
                    });
                    expect(source1.clock.startTime).toEqual(interval.start);
                    expect(source1.clock.stopTime).toEqual(interval.stop);
                });
            });
        });
    });

    it('dropError is raised on exception', function() {
        var mockEvent = {
            dataTransfer : {
                files : [{
                    name : 'czml1.czml',
                    czmlString : 'bad JSON'
                }]
            },
            stopPropagation : function() {
            },
            preventDefault : function() {
            }
        };

        viewer = createViewer(container);
        viewer.extend(viewerDragDropMixin);

        var spyListener = jasmine.createSpy('listener');

        viewer.dropError.addEventListener(spyListener);
        DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

        return pollToPromise(function() {
            return spyListener.calls.any();
        }).then(function() {
            expect(spyListener).toHaveBeenCalledWith(viewer, 'czml1.czml', jasmine.any(SyntaxError));

            viewer.dropError.removeEventListener(spyListener);
        });
    });

    it('dropError is raised FileReader error', function() {
        var mockEvent = {
            dataTransfer : {
                files : [{
                    name : 'czml1.czml',
                    errorMessage : 'bad JSON'
                }]
            },
            stopPropagation : function() {
            },
            preventDefault : function() {
            }
        };

        viewer = createViewer(container);
        viewer.extend(viewerDragDropMixin);

        var spyListener = jasmine.createSpy('listener');

        viewer.dropError.addEventListener(spyListener);
        DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

        return pollToPromise(function() {
            return spyListener.calls.any();
        }).then(function() {
            expect(spyListener).toHaveBeenCalledWith(viewer, mockEvent.dataTransfer.files[0].name, mockEvent.dataTransfer.files[0].errorMessage);

            viewer.dropError.removeEventListener(spyListener);
        });
    });

    function MockContainer() {
        var events = {};
        this.events = events;

        this.addEventListener = function(name, func, bubble) {
            events[name] = {
                func : func,
                bubble : bubble
            };
        };

        this.removeEventListener = function(name, func, bubble) {
            var subscribed = events[name];
            expect(subscribed.func).toBe(func);
            expect(subscribed.bubble).toEqual(bubble);
            delete events[name];
        };
    }

    it('enable/disable subscribes to provided dropTarget.', function() {
        var dropTarget = new MockContainer();

        viewer = createViewer(container);
        viewer.extend(viewerDragDropMixin, {
            dropTarget : dropTarget
        });

        expect(dropTarget.events.drop).toBeDefined();
        expect(dropTarget.events.dragenter).toBeDefined();
        expect(dropTarget.events.dragover).toBeDefined();
        expect(dropTarget.events.dragexit).toBeDefined();

        viewer.dropEnabled = false;
        expect(dropTarget.events.drop).toBeUndefined();
        expect(dropTarget.events.dragenter).toBeUndefined();
        expect(dropTarget.events.dragover).toBeUndefined();
        expect(dropTarget.events.dragexit).toBeUndefined();

        viewer.dropEnabled = true;
        expect(dropTarget.events.drop).toBeDefined();
        expect(dropTarget.events.dragenter).toBeDefined();
        expect(dropTarget.events.dragover).toBeDefined();
        expect(dropTarget.events.dragexit).toBeDefined();
    });

    it('can set new dropTarget.', function() {
        var dropTarget1 = new MockContainer();
        var dropTarget2 = new MockContainer();

        viewer = createViewer(container);
        viewer.extend(viewerDragDropMixin, {
            dropTarget : dropTarget1
        });

        expect(dropTarget1.events.drop).toBeDefined();
        expect(dropTarget1.events.dragenter).toBeDefined();
        expect(dropTarget1.events.dragover).toBeDefined();
        expect(dropTarget1.events.dragexit).toBeDefined();

        viewer.dropTarget = dropTarget2;
        expect(dropTarget1.events.drop).toBeUndefined();
        expect(dropTarget1.events.dragenter).toBeUndefined();
        expect(dropTarget1.events.dragover).toBeUndefined();
        expect(dropTarget1.events.dragexit).toBeUndefined();

        expect(dropTarget2.events.drop).toBeDefined();
        expect(dropTarget2.events.dragenter).toBeDefined();
        expect(dropTarget2.events.dragover).toBeDefined();
        expect(dropTarget2.events.dragexit).toBeDefined();
    });

    it('can set proxy.', function() {
        var proxy = {};

        viewer = createViewer(container);
        viewer.extend(viewerDragDropMixin, {
            proxy : proxy
        });
        expect(viewer.proxy).toBe(proxy);
    });

    it('throws with undefined viewer', function() {
        expect(function() {
            viewerDragDropMixin(undefined);
        }).toThrowDeveloperError();
    });

    it('throws with non-existant string container', function() {
        viewer = createViewer(container);
        expect(function() {
            viewer.extend(viewerDragDropMixin, {
                dropTarget : 'doesNotExist'
            });
        }).toThrowDeveloperError();
    });

    it('throws if dropTarget property already added by another mixin.', function() {
        viewer = createViewer(container);
        viewer.dropTarget = true;
        expect(function() {
            viewer.extend(viewerDragDropMixin);
        }).toThrowDeveloperError();
    });

    it('throws if dropEnabled property already added by another mixin.', function() {
        viewer = createViewer(container);
        viewer.dropEnabled = true;
        expect(function() {
            viewer.extend(viewerDragDropMixin);
        }).toThrowDeveloperError();
    });

    it('throws if dropError property already added by another mixin.', function() {
        viewer = createViewer(container);
        viewer.dropError = true;
        expect(function() {
            viewer.extend(viewerDragDropMixin);
        }).toThrowDeveloperError();
    });

    it('throws if clearOnDrop property already added by another mixin.', function() {
        viewer = createViewer(container);
        viewer.clearOnDrop = true;
        expect(function() {
            viewer.extend(viewerDragDropMixin);
        }).toThrowDeveloperError();
    });

    it('throws if flyToOnDrop property already added by another mixin.', function() {
        viewer = createViewer(container);
        viewer.flyToOnDrop = true;
        expect(function() {
            viewer.extend(viewerDragDropMixin);
        }).toThrowDeveloperError();
    });

    it('setting dropTarget to undefined throws exception', function() {
        viewer = createViewer(container);
        viewer.extend(viewerDragDropMixin);
        expect(function() {
            viewer.dropTarget = undefined;
        }).toThrowDeveloperError();
    });
}, 'WebGL');
